﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Markup;

namespace Geometry
{
    public sealed class RelativePoint : GObjectBase, IGPoint, IDraggable
    {
        public RelativePoint()
        {
            UpdateBoundingBox();
        }

        public RelativePoint(IGPoint _related, Vector _offset)
        {
            this.RelatedPoint = _related;
            this.OffX = _offset.X;
            this.OffY = _offset.Y;
            UpdateBoundingBox();
        }

        public RelativePoint(IGPoint _related, double _offX, double _offY)
        {
            this.RelatedPoint = _related;
            this.OffX = _offX;
            this.OffY = _offY;
            UpdateBoundingBox();
        }

        protected override void ReleaseDependencies()
        {
            this.RelatedPoint = null;
        }

        protected override void UpdateBoundingBox()
        {
            base.BoundingBox = new Rect(Position, Position);
        }

        private IGPoint m_RelatedPoint;

        [ValueSerializer(typeof(ElementReferenceSerializer))]
        [TypeConverter(typeof(PlaceHolderTypeConverter))]
        public IGPoint RelatedPoint
        {
            get
            {
                return m_RelatedPoint;
            }
            set
            {
                if (m_RelatedPoint == value) return;
                
                if (m_RelatedPoint != null) this.RelatedPoint.PropertyChanged -= new PropertyChangedEventHandler(_related_PropertyChanged);
                m_RelatedPoint = value;
                if (m_RelatedPoint != null) this.RelatedPoint.PropertyChanged += new PropertyChangedEventHandler(_related_PropertyChanged);

                RaisePropertyChanged("RelatedPoint");
                UpdatePosition();
            }
        }

        private Vector m_Offset;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double OffX
        {
            get
            {
                return m_Offset.X;
            }
            set
            {
                if (value.Equals(m_Offset.X)) return;
                m_Offset.X = value;
                RaisePropertyChanged("OffX");
                UpdatePosition();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public double OffY
        {
            get
            {
                return m_Offset.Y;
            }
            set
            {
                if (value.Equals(m_Offset.Y)) return;
                m_Offset.Y = value;
                RaisePropertyChanged("OffY");
                UpdatePosition();
            }
        }

        public Vector Offset
        {
            get
            {
                return m_Offset;
            }
            set
            {
                if (value.Equals(m_Offset)) return;
                m_Offset = value;
                RaisePropertyChanged("Offset");
                RaisePropertyChanged("OffX");
                RaisePropertyChanged("OffY");
                UpdatePosition();
            }
        }

        void _related_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "Position") return;

            UpdatePosition();
        }

        private void UpdatePosition()
        {
            if (this.RelatedPoint.IsNullOrPlaceHolder()) return;
            this.Position = Point.Add(this.RelatedPoint.Position, this.Offset);
        }

        #region IGPoint Members
        private Point m_Position;

        public double PosX
        {
            get { return m_Position.X; }
            private set
            {
                if (value.Equals(m_Position.X)) return;
                m_Position.X = value;
                UpdateBoundingBox();
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("Position");
            }
        }

        public double PosY
        {
            get { return m_Position.Y; }
            private set
            {
                if (value.Equals(m_Position.Y)) return;
                m_Position.Y = value;
                UpdateBoundingBox();
                RaisePropertyChanged("PosY");
                RaisePropertyChanged("Position");
            }
        }

        public Point Position
        {
            get { return m_Position; }
            private set
            {
                if (value.Equals(m_Position)) return;
                m_Position = value;
                UpdateBoundingBox();
                RaisePropertyChanged("Position");
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("PosY");
            }
        }

        #endregion

        #region IDraggable Members
        private Vector m_BeginDragOffset;
        private bool m_IsDragged = false;

        public void BeginDrag()
        {
            m_BeginDragOffset = m_Offset;
        }

        public void EndDrag()
        {
            m_IsDragged = false;
        }

        public void CancelDrag()
        {
            Offset = m_BeginDragOffset;
        }

        public void DragBy(Vector amount)
        {
            Offset += amount;
            m_IsDragged = true;
        }

        public bool IsDragged
        {
            get { return m_IsDragged; }
        }

        #endregion
    }
}
